//== GAIA ============================================================================

//== BEGINNING OF CODE ===============================================================

#include "sagittarius/core/SagittariusCore.h"


#include <stdio.h>
#include <malloc.h>
#include <assert.h>
#include <math.h>
#include "sagittarius/parallel/atomics.h"
#include "sagittarius/parallel/thread.h"
#include "sagittarius/parallel/multisema.h"
#include "sagittarius/io/io.h"

//#define _GNU_SOURCE
//#define __USE_GNU
#include <unistd.h>
//#include <linux/sema.h>
//#include <time.h>
#include <sys/time.h>
#include <sys/syscall.h>
//#include <sched.h>
//#include <errno.h>

SG_DEF_MULTISEMA(sema);
uint32 nworkers = 0;
uint32 nreaders = 0;
uint32 nwriters = 0;
uint32 totalrun = 0;

int standinrand() {
    //sg_write(2, "sar\n", sizeof("sar\n")); //helps prevent inlining
    return 10;
}

#define READER_LOCK     RLSEMA(sema);
#define READER_UNLOCK   RUSEMA(sema);

void target_reader(void *arg) {
#   define TM_BASE 10000
#   define TM_VAR 1000
    //sg_write(2, "target_reader\n", sizeof("target_reader\n"));
    struct timespec t = {0, TM_BASE+(standinrand()%TM_VAR)*TM_BASE};
    sg_fad32(&totalrun,1);
    sg_fad32(&nreaders,1);
    READER_LOCK
    syscall(SYS_nanosleep, &t, NULL);
    READER_UNLOCK
    sg_fad32(&nreaders,-1);
    sg_exit_thread(0);
}

#define WRITER_LOCK     WLSEMA(sema);
#define WRITER_UNLOCK   WUSEMA(sema);

void target_writer(void *arg) {
#   define WTM_BASE 100000
#   define WTM_VAR 1000
    struct timespec t = {0, WTM_BASE+(standinrand()%WTM_VAR)*WTM_BASE};
    sg_fad32(&totalrun,1);
    sg_fad32(&nwriters,1);
    WRITER_LOCK
    syscall(SYS_nanosleep, &t, NULL);
    WRITER_UNLOCK
    sg_fad32(&nwriters,-1);
    sg_exit_thread(0);
}

#define N_SPAWN      100
#define WRITER_RATIO 10
struct timespec tfinish = {0, 100000000}; //100msec

int main(int argc, char* argv[])
{
    int i;
    spawnresult res=0;
    int haveerr = 0;
    INITSEMA(sema);
    //spawn
    for(i=0; i<N_SPAWN; ++i) {
        //sg_write(2, "spawn loop\n", sizeof("spawn loop\n"));
        //if((rand()%WRITER_RATIO) != 0) {
        if((i%WRITER_RATIO) != 0) {
            //sg_write(2, "spawn reader\n", sizeof("spawn reader\n"));
            res = sg_spawn(target_reader, NULL);
            if(!spawn_successful(res)) {
                haveerr = 1;
                break;
            }
        } else {
            //sg_write(2, "spawn writer\n", sizeof("spawn writer\n"));
            res = sg_spawn(target_writer, NULL);
            if(!spawn_successful(res)) {
                haveerr = 1;
                break;
            }
        }
        /*sg_write(2, "res = ", sizeof("res = "));
        sg_writeInt2(res);
        sg_write(2, "\n", sizeof("\n"));*/
    }
    
    if(haveerr) {
        sg_write(2, "Run incomplete. Errors detected.\n", sizeof("Run incomplete. Errors detected.\n"));
        //perror("Run incomplete");
        return 1;
    }
    
    nworkers = nreaders + nwriters;
    while(nworkers) {
        sg_write(2, "Waiting on ", sizeof("Waiting on "));
        sg_writeUInt2(nworkers);
        sg_write(2, " processes (", sizeof(" processes ("));
        sg_writeUInt2(nreaders);
        sg_write(2, " readers and ", sizeof(" readers and "));
        sg_writeUInt2(nwriters);
        sg_write(2, " writers) to finish\n", sizeof(" writers) to finish\n"));
        
        /*sg_write(2, "sema state: ", sizeof("sema state: "));
        sg_writeUInt2(sema);
        sg_write(2, "\n", sizeof("\n"));
        
        sg_write(2, "Total run: ", sizeof("Total run: "));
        sg_writeUInt2(totalrun);
        sg_write(2, "\n", sizeof("\n"));*/
        
        syscall(SYS_nanosleep, &tfinish, NULL);
        nworkers = nreaders + nwriters;
    }
    
    sg_write(2, "Total run: ", sizeof("Total run: "));
    sg_writeUInt2(totalrun);
    sg_write(2, "\nAll processes finished\n", sizeof("\nAll processes finished\n"));
    
    return 0;
}
